#include "stdafx.h"
#include "ORB_Objects.hpp"
#include <tao/ObjectIdListC.h>

using std::vector;
using std::string;

ORB_Objects::ORB_Objects (void)
{
}

ORB_Objects::ORB_Objects( CORBA::ORB_var& orb, const char* name_server )
{
    init(orb,name_server);
}

ORB_Objects::~ORB_Objects()
{
}

void
ORB_Objects::init (CORBA::ORB_var& orb, const char* name_server)
{
    this->root_orb_ = orb;

    //ACE_DEBUG((LM_DEBUG,"(%P|%t) ORB_Objects::init\n"));

    {
        CORBA::Object_var object = this->root_orb_->resolve_initial_references("RootPOA");
        this->root_poa_ = PortableServer::POA::_narrow (object.in());
        this->root_poa_->the_POAManager()->activate();
    }

    //ACE_DEBUG((LM_DEBUG,"(%P|%t) ORB_Objects::init Resolve the RTORB.\n"));

    {
        // Resolve the RTORB.
        CORBA::Object_var object = this->root_orb_->resolve_initial_references ("RTORB");
        this->rt_orb_ = RTCORBA::RTORB::_narrow (object.in());
    }

    //ACE_DEBUG((LM_DEBUG,"(%P|%t) ORB_Objects::init Create an RT POA\n"));

    {
        // Create an RT POA with a lane at the given priority.
        CORBA::Policy_var activation_policy = root_poa_->create_implicit_activation_policy (PortableServer::IMPLICIT_ACTIVATION);
        // Create a priority model policy.
        CORBA::Policy_var priority_model_policy = rt_orb_->create_priority_model_policy (RTCORBA::CLIENT_PROPAGATED, 0);
        // Create the thread-pool.
        RTCORBA::ThreadpoolId threadpool_id = rt_orb_->create_threadpool(
            CORBA::ULong(0),        // stacksize,
            CORBA::ULong(1),        // static_threads,
            CORBA::ULong(0),        // dynamic_threads,
            RTCORBA::Priority(0),   // default_priority,
            CORBA::Boolean(0),      // allow_request_buffering,
            CORBA::ULong(0),        // max_buffered_requests,
            CORBA::ULong(0)         // max_request_buffer_size
            );
    
        CORBA::Policy_var thread_pool_policy = rt_orb_->create_threadpool_policy (threadpool_id);
    
        CORBA::PolicyList poa_policy_list;
        poa_policy_list.length (3);
        poa_policy_list[0] = priority_model_policy;
        poa_policy_list[1] = activation_policy;
        poa_policy_list[2] = thread_pool_policy;
    
        try{
            this->rt_poa_ = root_poa_->find_POA ("RT POA!",true);
        }
        catch( PortableServer::POA::AdapterNonExistent & /*ex*/){
            this->rt_poa_ = root_poa_->create_POA ("RT POA!", root_poa_->the_POAManager (), poa_policy_list);
        }
    }

    //{
    //    // Resolve the Current
    //    CORBA::Object_var object = this->root_orb_->resolve_initial_references ("RTCurrent");
    //    this->rt_current_ = RTCORBA::Current::_narrow (object.in());
    //    this->rt_current_->the_priority (0);
    //}

    //{
    //    // Resolve the Current
    //    CORBA::Object_var object = this->root_orb_->resolve_initial_references ("POACurrent");
    //    this->poa_current_ = PortableServer::Current::_narrow (object.in());
    //}

    //ACE_DEBUG((LM_DEBUG,"(%P|%t) ORB_Objects::init Resolve the Naming service\n"));

    {
        // Resolve the Naming service
        if( name_server == NULL ){
            ACE_DEBUG((LM_DEBUG,"(%P|%t) ORB_Objects::init resolve_initial_references NameService\n"));

            CORBA::Object_var object = this->root_orb_->resolve_initial_references ("NameService");
            this->naming_ = CosNaming::NamingContext::_narrow (object.in());
        }
        else{
            std::string ns = _check_naming(name_server);
            try{
                CORBA::Object_var object = this->root_orb_->string_to_object(ns.c_str());
                if (CORBA::is_nil (object.in())){
                    ACE_ERROR((LM_ERROR, ACE_TEXT("%I(%P|%t) Unable to initialize the NameService.\n")));
                    return;
                }

                //ACE_DEBUG((LM_DEBUG,"(%P|%t) ORB_Objects::init register_initial_reference NameService\n"));

                // Reference to the root Naming Context.
                //this->naming_ = CosNaming::NamingContextExt::_narrow (object.in ());
                this->naming_ = CosNaming::NamingContext::_narrow (object.in ());
                this->root_orb_->register_initial_reference("NameService",this->naming_.in());
            }
            catch( const CORBA::UserException& userex ){
                ACE_ERROR((LM_ERROR,ACE_TEXT("%I(%P|%t) UserException when resolve NameService %C\n"), ns.c_str()));
                userex._tao_print_exception("CORBA::UserException");
            }
            catch( CORBA::SystemException & /*ex*/){
                ACE_ERROR((LM_ERROR,ACE_TEXT("%I(%P|%t) SystemException when resolve NameService %C\n"), ns.c_str()));
            }
        }
    }
}

int ORB_Objects::rebind_name( std::string name, CORBA::Object_ptr object )
{
    if( name.empty() )
        return -1;

    try
    {
        // create the name for the naming service
        CosNaming::Name svc_name (1);
        svc_name.length (1);

        vector<string> vec;
        cppx::fuStringHelper(name).Split(".",vec);
        if( vec.size() == 1 ){
            svc_name[0].id   = CORBA::string_dup (name.c_str());
        }
        else{
            svc_name[0].id   = CORBA::string_dup (vec[0].c_str());
            svc_name[0].kind = CORBA::string_dup (vec[1].c_str());
        }

        // rebind the object.
        naming_->rebind(svc_name,object);
    }
    catch(CosNaming::NamingContext::AlreadyBound const &)
    {
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to rebind %C\n"),name.c_str()), -1);
    }
    catch( CORBA::SystemException const & /*ex*/){
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to rebind %C\n"),name.c_str()), -1);
    }
    catch( CORBA::UserException const & /*userex*/ ){
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to rebind %C\n"),name.c_str()), -1);
    }
    catch( CORBA::Exception const & /*ex*/ ){
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to rebind %C\n"),name.c_str()), -1);
    }
    return 0;
}

int ORB_Objects::unbind_name( std::string name )
{
    try
    {
        // create the name for the naming service
        CosNaming::Name svc_name (1);
        svc_name.length (1);

        vector<string> vec;
        cppx::fuStringHelper(name).Split(".",vec);
        if( vec.size() == 1 ){
            svc_name[0].id   = CORBA::string_dup (name.c_str());
        }
        else{
            svc_name[0].id   = CORBA::string_dup (vec[0].c_str());
            svc_name[0].kind = CORBA::string_dup (vec[1].c_str());
        }

        // unbind the naming_object.
        naming_->unbind(svc_name);
    }
    catch( CosNaming::NamingContext::AlreadyBound const & )
    {
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to unbind %C\n"),name.c_str()), -1);
    }
    catch( CORBA::SystemException const & /*ex*/){
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to unbind %C\n"),name.c_str()), -1);
    }
    catch( CORBA::UserException const & /*userex*/ ){
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to unbind %C\n"),name.c_str()), -1);
    }
    catch( CORBA::Exception const & /*ex*/ ){
        ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Unable to unbind %C\n"),name.c_str()), -1);
    }
    return 0;
}

CORBA::Object_ptr ORB_Objects::resolve( const char *name,const char* name_server /*= NULL*/ )
{
    CosNaming::Name svc_name (1);
    svc_name.length (1);

    vector<string> vec;
    cppx::fuStringHelper(name).Split(".",vec);
    if( vec.size() == 1 ){
        svc_name[0].id   = CORBA::string_dup (name);
    }
    else{
        svc_name[0].id   = CORBA::string_dup (vec[0].c_str());
        svc_name[0].kind = CORBA::string_dup (vec[1].c_str());
    }

    try{
        if( name_server == NULL ){
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("Resolve [%C] on [Local NameService]\n"),name));
            CORBA::Object_var obj = naming_->resolve (svc_name);
            return obj._retn();
        }
        else{
            std::string ns = _check_naming(name_server);
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("Resolve [%C] on [%C]\n"),name,ns.c_str()));

            CORBA::Object_var naming_object = this->root_orb_->string_to_object(ns.c_str());
            if( CORBA::is_nil (naming_object.in()) ){
                ACE_ERROR((LM_ERROR, ACE_TEXT("Unknown NameServer [%C].\n"), ns.c_str()));
            }
            else{
                CosNaming::NamingContext_var temp_naming_ = CosNaming::NamingContext::_narrow (naming_object);
                CORBA::Object_var obj = temp_naming_->resolve (svc_name);
                return obj._retn();
            }
        }
    }
    catch( CORBA::SystemException const &){}
    catch( CORBA::UserException const &){}
    catch( CORBA::Exception const &){}

    //ACE_ERROR((LM_ERROR, ACE_TEXT("Resolve [%C] on [%C] failed.\n"), name,ns.c_str()));
    return CORBA::Object::_nil();
}

CosNotifyChannelAdmin::EventChannelFactory_ptr
ORB_Objects::resolve_notify_factory (const char* name_server)
{
    try{
        CORBA::Object_var obj = resolve("NotifyEventChannelFactory",name_server);
        CosNotifyChannelAdmin::EventChannelFactory_var ecf =
            CosNotifyChannelAdmin::EventChannelFactory::_narrow (obj.in());
        return ecf._retn();
    }
    catch( CORBA::SystemException const &){}
    catch( CORBA::UserException const &){}
    catch( CORBA::Exception const &){}

    return CosNotifyChannelAdmin::EventChannelFactory::_nil();
}

CosNotifyChannelAdmin::EventChannel_ptr
ORB_Objects::resolve_notify_channel( const char* name_server,int ec_id )
{
    std::string ns = _check_naming(name_server);
	ACE_DEBUG((LM_DEBUG, ACE_TEXT("%I(%P|%t) Resolve channel[%d] on [%C].\n"), ec_id,ns.c_str()));

	try{
        // Resolve the Notification Factory.
        CosNotifyChannelAdmin::EventChannelFactory_var ecf = resolve_notify_factory(name_server);
        if( CORBA::is_nil(ecf) ){
            ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("Nil EventChannelFactory_var on [%C]\n"),ns.c_str()),NULL);
        }
        
        // Find the EventChannel created by the supplier.
        CosNotifyChannelAdmin::ChannelIDSeq_var channel_seq = ecf->get_all_channels();
        if( channel_seq->length() == 0 ){
            ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("No Event Channel active!\n")),NULL);
        }
        CosNotifyChannelAdmin::EventChannel_var ec = ecf->get_event_channel(channel_seq[ec_id]);
        return ec._retn();
    }
    catch( CORBA::SystemException const &){}
    catch( CORBA::UserException const &){}
    catch( CORBA::Exception const &){}

    //ACE_ERROR((LM_ERROR, ACE_TEXT("%I(%P|%t) Resolve [channel %d] on [%C] failed.\n"), ec_id,ns.c_str()));
    return NULL;
}

CosNotifyChannelAdmin::EventChannel_ptr
ORB_Objects::create_event_channel( int ec_thread_count_ )
{
    CosNotifyChannelAdmin::EventChannelFactory_var ecf = this->resolve_notify_factory();
	if( CORBA::is_nil(ecf) )
		return NULL;

    // Create an EventChannel
    CosNotification::QoSProperties qos;
    CosNotification::AdminProperties admin;

    // Create an event channel
    CosNotifyChannelAdmin::ChannelID id;
    CosNotifyChannelAdmin::EventChannel_var ec = ecf->create_channel (qos, admin, id);

    // Set the Qos
    // See $TAO_ROOT/orbsvcs/orbsvcs/NotifyExt.idl
    if( ec_thread_count_ && !CORBA::is_nil(ec) ){
        NotifyExt::ThreadPoolParams tp_params = {
            NotifyExt::CLIENT_PROPAGATED,   // NotifyExt::PriorityModel priority_model;
            0,                              // NotifyExt::Priority server_priority;
            0,                              // CORBA::ULong stacksize;
            ec_thread_count_,               // CORBA::ULong static_threads;
            0,                              // CORBA::ULong dynamic_threads;
            0,                              // NotifyExt::Priority default_priority;
            0,                              // CORBA::Boolean allow_request_buffering;
            0,                              // CORBA::ULong max_buffered_requests;
            0                               // CORBA::ULong max_request_buffer_size;
        };

        CosNotification::QoSProperties qos (1);
        qos.length (1);
        qos[0].name = CORBA::string_dup (NotifyExt::ThreadPool);
        qos[0].value <<= tp_params;

        // Note that instead of <set_qos>, the <qos> can also be passed while creating the channel.
        ec->set_qos (qos);
    }

    ACE_DEBUG ((LM_DEBUG, "Created Event Channel with %d threads\n", ec_thread_count_));
    return ec._retn();
}

CosNotifyChannelAdmin::SupplierAdmin_ptr 
ORB_Objects::create_supplier_admin( CosNotifyChannelAdmin::EventChannel_ptr ec )
{
    if( CORBA::is_nil(ec) )
        return NULL;
    
    // Create a Supplier Admin
    CosNotifyChannelAdmin::AdminID adminid = 0;
    CosNotifyChannelAdmin::SupplierAdmin_var supplier_admin = ec->new_for_suppliers (CosNotifyChannelAdmin::AND_OP, adminid);
    ACE_ASSERT(!CORBA::is_nil(supplier_admin.in()));

    return supplier_admin._retn();
}

CosNotifyChannelAdmin::ConsumerAdmin_ptr
ORB_Objects::create_consumer_admin( CosNotifyChannelAdmin::EventChannel_ptr ec )
{
    if( CORBA::is_nil(ec) )
        return 0;

    // Create a TAO_Notify_Consumer Admin
    CosNotifyChannelAdmin::AdminID adminid = 0;
    CosNotifyChannelAdmin::ConsumerAdmin_var consumer_admin = ec->new_for_consumers(CosNotifyChannelAdmin::AND_OP, adminid);
    ACE_ASSERT(!CORBA::is_nil(consumer_admin.in()));

    return consumer_admin._retn();
}

std::string ORB_Objects::_check_naming( std::string naming )
{
    cppx::fuStringHelper helper(naming);
    if( helper.StartsWith("corbaname::") ||
        helper.StartsWith("corbaname:iiop:") )
        return naming;
    else
        return std::string("corbaname::") + naming;
}
